﻿using MJPEGDecoderWinRT.Common;
using MJPEGDecoderWinRT.Model;
using MJPEGDecoderWinRTLib;
using System;
using System.Diagnostics;
using System.Windows.Input;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace MJPEGDecoderWinRT.ViewModel
{
    class ViewCameraPageViewModel: BindableBase
    {
        private Frame _frame;
        Camera _camera;
        BitmapImage _bitmap;
        string _errorMsg;
        string _requestUri;
        double _fps;
        MJPEGDecoder _mjpegDecoder;

        TRelayCommand<object> _startCameraStreamCmd;
        TRelayCommand<object> _stopCameraStreamCmd;

        Stopwatch _stopWatch;
        int _nrOfFramesReceived;

        public ViewCameraPageViewModel(Frame frame, Camera camera)
        {
            _frame = frame;
            _camera = camera;
            _bitmap = new BitmapImage();
            _errorMsg = "";
            _mjpegDecoder = new MJPEGDecoder();
        }

        public Camera Camera
        {
            get { return _camera; }
            set { _camera = value; }
        }

        public BitmapImage CameraBitmap
        {
            get { return _bitmap; }
            set { SetProperty(ref _bitmap, value); }
        }

        public string ErrorMsg
        {
            get { return _errorMsg; }
            set { SetProperty(ref _errorMsg, value); }
        }

        public string RequestUri
        {
            get { return _requestUri; }
            set { SetProperty(ref _requestUri, value); }
        }

        public double FPS
        {
            get { return _fps; }
            set { SetProperty(ref _fps, value); }
        }

        #region Commands

        public ICommand StartCameraStreamCmd
        {
            get
            {
                if (_startCameraStreamCmd == null)
                {
                    _startCameraStreamCmd = new TRelayCommand<object>(
                        async (o) =>
                        {
                            // Register listener methods
                            _mjpegDecoder.FrameReady += mjpegDecoder_FrameReady;
                            _mjpegDecoder.Error += mjpegDecoder_Error;

                            // Construct Http Uri
                            RequestUri = String.Format("{0}/{1}", Camera.Uri, Camera.CameraResourcePath);

                            // Prepare StopWatch for calculating fps
                            _stopWatch = new Stopwatch();
                            _stopWatch.Start();
                            _nrOfFramesReceived = 0;
                            FPS = 0;

                            // Only for test (with a Foscam camera). No Http authentication,
                            // user/password is provided (and visible) in the Uri path.
                            //                            _mjpegDecoder.ParseStreamAsync("http://192.168.0.219:8081/videostream.cgi?user=username&pwd=password");
                            // Tell MJPEGDecoder to connect to the IP camera, parse the mjpeg stream, and 
                            // report the received image frames.
                            await _mjpegDecoder.ParseStreamAsync(RequestUri, Camera.UserName, Camera.Password);
                        });
                }
                return _startCameraStreamCmd;
            }
        }

        public ICommand StopCameraStreamCmd
        {
            get
            {
                if (_stopCameraStreamCmd == null)
                {
                    _stopCameraStreamCmd = new TRelayCommand<object>(
                        (o) =>
                        {
                            _mjpegDecoder.StopStream();
                        });
                }
                return _stopCameraStreamCmd;
            }
        }

        #endregion

        #region Helpers

        private async void mjpegDecoder_FrameReady(object sender, FrameReadyEventArgs e)
        {
            // Copy the received FrameBuffer to an InMemoryRandomAccessStream.
            using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
            {

                using (DataWriter writer = new DataWriter(ms.GetOutputStreamAt(0)))
                {
                    writer.WriteBytes(e.FrameBuffer);
                    await writer.StoreAsync();
                }
                // Update source of CameraBitmap with the memory stream
                CameraBitmap.SetSource(ms);
            }

            // Calculate fps
            if ( ++_nrOfFramesReceived == 50 )
            {
                long msElapsed = _stopWatch.ElapsedMilliseconds;
                FPS = 50000.0 / (double)msElapsed;
                _nrOfFramesReceived = 0;
                _stopWatch.Restart();
            }
        }

        private void mjpegDecoder_Error(object sender, ErrorEventArgs e)
        {
            ErrorMsg = e.Message;
        }

        #endregion

    }
}
